package logging;

import java.util.ArrayList;

import Exception.CustomNotImplementsFilterStrategy;
import Exception.CustomNotImplementsICustom;
import Exception.InvalidLevelException;
import Exception.NonExistingConstructorException;
import Filter.CustomFilter;
import Filter.Filter;
import Handler.CustomHandler;
import Handler.Handler;

public class Logger implements ILogger{
	
	private Level levelLogger;
	private boolean isOff;
	private String loggerName;
	private loggerConfig config;
	private ArrayList<Handler> handlers;
	
	public Logger(String name) {
		this.loggerName = name;
		this.loadConfig();
		if (levelLogger == Level.OFF)
			this.isOff = true;
		else 
			this.isOff = false;
	}
	
	public void trace(String msg){
		this.log(Level.TRACE, msg);
	}
	
	public void trace(String msg, Throwable throwable){
		this.log(Level.TRACE, msg, throwable);
	}
	
	public void debug(String msg){
		this.log(Level.DEBUG, msg);
	}
	
	public void debug(String msg, Throwable throwable){
		this.log(Level.DEBUG, msg, throwable);
	}
	
	public void info(String msg){
		this.log(Level.INFO, msg);
	}
	
	public void info(String msg, Throwable throwable){
		this.log(Level.INFO, msg, throwable);
	}
	
	public void warn(String msg){
		this.log(Level.WARN, msg);
	}
	
	public void warn(String msg, Throwable throwable){
		this.log(Level.WARN, msg, throwable);
	}
	
	public void error(String msg){
		this.log(Level.ERROR, msg);
	}
	
	public void error(String msg, Throwable throwable){
		this.log(Level.ERROR, msg, throwable);
	}
	
	public void fatal(String msg){
		this.log(Level.FATAL, msg);
	}
	
	public void fatal(String msg, Throwable throwable){
		this.log(Level.FATAL, msg, throwable);
	}
	
	public void off(){
		this.isOff = true;
	}
	
	public void on(){
		this.isOff = false;
	}
	
	public void setLevel(String level) throws InvalidLevelException{
		levelLogger = Level.stringToLevel(level);
	}
	
	public Level getLevel(){
		return levelLogger;
	}
	
	public void log(Level levelMsg, String msg){
		Record record = new Record(msg, levelMsg, loggerName);
		record.setFormatConsole(config.getFormatConsole());
		record.setFormatFile(config.getFormatFiles());
		record.setSeparator(config.getSeparator());
		record.setTypeFormat(config.getTypeFormat());
		log(record);
	}
	
	public void log(Level levelMsg, String msg, Throwable throwable){
		Record record = new Record(msg, levelMsg, loggerName);
		record.setFormatConsole(config.getFormatConsole());
		record.setFormatFile(config.getFormatFiles());
		record.setSeparator(config.getSeparator());
		record.setThrowable(throwable);
		record.setTypeFormat(config.getTypeFormat());
		log(record);
	}
	
	/*
	 * log the record to all outputs if applicable 
	 */
	public void log(Record record){
		if (!this.isOff){
			for (Handler handler : handlers){
				if (levelLogger.compareWith(record.getLevel()) == 0){
					handler.write(record);
				}
			}
		}
	}
	
	/* 
	 * load configuration from propertie
	 */
	private void loadConfig(){
		config = new loggerConfig("Configuration/config_2");
		config.readConfig();
		try{
			this.levelLogger = Level.stringToLevel(config.getLevelConfig());
		}catch (InvalidLevelException e){
			e.printStackTrace();
		}
	
		HandlerMaker h = new HandlerMaker(config);
		handlers = h.createHandler();
	}
	
	/*
	 * add custom destination to handler list
	 */
	public void addCustomDestination(String implementor, Object[] params) throws ClassNotFoundException, NoSuchMethodException, SecurityException, NonExistingConstructorException, CustomNotImplementsICustom{
		handlers.add(new CustomHandler(implementor, params));
	}

	@Override
	public ArrayList<Handler> getHandlerList() {
		return handlers;
	}
	
	public void addFilterCustomToHandler(String implementor, Object[] params, String name) throws ClassNotFoundException, NoSuchMethodException, SecurityException, NonExistingConstructorException, CustomNotImplementsFilterStrategy{
		Handler handler = foundFileHandlerWithPath(name);
		if (handler != null){
			handler.setFilter(new Filter(new CustomFilter(implementor, params)));
		}else{
			handler = HandlerMaker.createHandler(name);
			handler.setFilter(new Filter(new CustomFilter(implementor, params)));
			handlers.add(handler);
		}
	}
	
	private Handler foundFileHandlerWithPath(String name){
		for (Handler handler : handlers){
			if (handler.getName().equals(name))
				return handler;
		}	
		return null;
	}

}